import { ElementSimStatus, AnimationActivityStatusNotice, ElementSimStatusColor, VariableTipType, AnimationInfo } from "./types/Status";
import { SimulateController } from "./Controller";
import { IpcMsg, MessageChannelType } from "./types/Ipc";
import { WinName } from "./types/Win";
import { HiddenStatus } from "./types/Controller";
import { DebouncedFunc, throttle, cloneDeep, isEqual } from "lodash";

function parseStringToJson(inputString: string) {
  const regex = /(\w+)\s*=\s*([^,\s]*)/g;
  const result: any = {};
  
  let match;
  while ((match = regex.exec(inputString)) !== null) {
    const key = match[1];
    const value = match[2] || null; // 如果值为空字符串，设为 null
    
    result[key] = value;
  }
  
  return result;
}

class SimStatus {

  controller: SimulateController;
  consumeDatas: DebouncedFunc<() => void>

  simStatusMap = new Map<ElementSimStatus, Map<string, AnimationActivityStatusNotice>>();
  diagramModelInfoMap = new Map<string, any>()
  
  constructor(c: SimulateController) {
    this.controller = c;
    this.consumeDatas = throttle(() => {
      if (app.global.animationInfosRecordMap.size === 0) return;
      this.sendAnimationInfosToDraw();
    }, this.controller.customizedTimerGap);
  }

  private filterAnimateInfosByLast(o: Map<string, AnimationInfo>, n: Map<string, AnimationInfo>) {
    for (const [k, v] of o.entries()) {
      const info = n.get(k);
      if (info && info.color === v.color && isEqual(info.tips, v.tips)) {
        n.delete(k);
      }
    }
    return n;
  }

  async serviceInit() {
    this.simStatusMap.clear();
    this.diagramModelInfoMap.clear();
    this.controller.ipcService.send(WinName.MainWindow, MessageChannelType.SimFinish);
    app.global.animationInfosRecordMap.clear();
    this.diagramModelInfoMap.clear();
  }

  private transferSimStatus(now: ElementSimStatus, before: ElementSimStatus, status: AnimationActivityStatusNotice) {
    const beforeMap = this.simStatusMap.get(before);
    if (beforeMap && beforeMap.get(status.modelId)) {
      beforeMap.delete(status.modelId);
    }
    const nowMap = this.simStatusMap.get(now);
    if (nowMap) {
      nowMap.set(status.modelId, status);
    } else {
      const initMap = new Map([[status.modelId, status]]);
      this.simStatusMap.set(now, initMap);
    }
  }

  /*
    * process
      * ElementSimStatus.ACTIVE --> ElementSimStatus.RUNNING --> ElementSimStatus.FROZEN
      * it's possible to cycle
  */
  async dealSimStatus(s: AnimationActivityStatusNotice) {
    // deal encrypt
    let imageSwitcherInfo = this.diagramModelInfoMap.get(s.diagramId);
    if (!imageSwitcherInfo) {
      imageSwitcherInfo = (await this.controller.modelApi.modelInfo({ projectId: this.controller.projectId, modelId: s.diagramId })).data?.data;
      this.diagramModelInfoMap.set(s.diagramId, imageSwitcherInfo);
    }
    if (imageSwitcherInfo && imageSwitcherInfo.hiddenStatus === HiddenStatus.HIDDEN_IN_ENCAP) return;
    switch (s.type) {
      case ElementSimStatus.ACTIVE:
        this.transferSimStatus(ElementSimStatus.ACTIVE, ElementSimStatus.FROZEN, s);
        break;
      case ElementSimStatus.RUNNING:
        this.transferSimStatus(ElementSimStatus.RUNNING, ElementSimStatus.ACTIVE, s);
        break;
      case ElementSimStatus.FROZEN:
        this.transferSimStatus(ElementSimStatus.FROZEN, ElementSimStatus.RUNNING, s);
        break;
      default:
    }
    // backword compatibility
    switch (this.controller.mdesignVersion) {
      case [4, 3, 0, 1].join(','):
        if (this.controller.toolbarService.isDiagramAutoSwitch) {
          this.controller.ipcService.send(WinName.MainWindow, MessageChannelType.SimSwitchDiagram, s.diagramId);
        }
        this.controller.ipcService.sendCustomize(
          IpcMsg.MarkShape,
          {
            projectId: this.controller.projectId,
            modelId: s.modelId,
            type: `simulation-${this.controller.projectId}${this.controller.modelId}`,
            color: ElementSimStatusColor[s.type],
            tips: this.getSimTips(s.modelId),
          }
        );
        break;
    
      default:
      {
        let diagramId = '';
        if (this.controller.toolbarService.isDiagramAutoSwitch) {
          diagramId =  s.diagramId;
        }
        const { passText, target } = await this.parseSignalData(s.data);
        app.global.animationInfosRecordMap.set(s.modelId, {
          projectId: this.controller.projectId,
          modelId: s.modelId,
          type: "simulation",
          statusType: s.type,
          color: ElementSimStatusColor[s.type],
          tips: this.getSimTips(s.modelId),
          passText,
          targetId: target,
          diagramId: diagramId,
          edgeRunTime: s.edgeRunTime,
        });
        this.consumeDatas();
      }
    }
  }

  sendAnimationInfosToDraw() {
    console.log("status info map: ", app.global.animationInfosRecordMap);
    this.controller.ipcService.send( // fresh m-design table
      WinName.MainWindow,
      MessageChannelType.SimAnimation,
      this.filterAnimateInfosByLast(app.global.animationInfosRecordLastMap, app.global.animationInfosRecordMap) // proxy object cannot be sended by electron-ipc
    );
    app.global.animationInfosRecordLastMap = cloneDeep(app.global.animationInfosRecordMap);
    app.global.animationInfosRecordMap.clear();
  }

  /*
    * has data?
      * data has the prior showing
      * no-data
        * show the element's status
    * the data can be json-parsed?
      * active diagram
      * parse-error
        * status machine
  */
  getSimTips(modelId: string) {
    let tips: string[] = [];
    let elementSimStatus = '' as ElementSimStatus;
    let activeElement = null;
    for (const [status, map] of this.simStatusMap.entries()) {
      activeElement = map.get(modelId);
      if (activeElement) {
        elementSimStatus = status;
        break;
      }
    }
    if (!activeElement) return;
    if (activeElement.data) {
      let tipsData: any[] = [];
      try { // activity diagram
        tipsData = JSON.parse(activeElement.data);
        if (tipsData.length) {
          for (const t of tipsData) {
            if (t.variableName && t.value !== undefined) {
              tips.push(`${t.variableName} : ${String(t.value)}`);
            } else {
              if (t.variableName) {
                tips.push(t.variableName);
              } else if (t.value) {
                tips.push(t.value);
              } else { // diagram no-data
                tips = [VariableTipType[this.controller.lang][elementSimStatus]];
              }
            }
          }
        } else { // diagram no-data
          tips = [VariableTipType[this.controller.lang][elementSimStatus]];
        }
      } catch (err: any) { // status machine
        tips = [activeElement.data];
      }
    } else { // use elements' status
      tips = [VariableTipType[this.controller.lang][elementSimStatus]];
    }
    return tips;
  }

  /**
   * 获取仿真传递文案
   */
  async parseSignalData(data: string) {
    try {
      if (!data) {
        return {} as any;
      }
      const res: any = {
        passText: '',
        target: ''
      };
      const json = JSON.parse(data);
      if (Array.isArray(json)) {
        for (let i = 0; i < json.length; i++) {
          const v = json[i];
          if (v.value != null && v.value !== '') {
            res.passText += v.value + ',';
          }
          if (v.target != null && v.target !== '') {
            res.target += v.target + ',';
          }
        }
        res.passText = res.passText.slice(0, -1);
        res.target = res.target.slice(0, -1);
      } else {
        if (json.value != null && json.value !== '') {
          res.passText = json.value;
        }
        // 连线仿真确定目标端，便于连接器或者其他图形确定动画运动方向
        if (json.target != null && json.target !== '') {
          res.target = json.target;
        }
      }
      return res;
    } catch (e) {
      console.log(e);
    }
    return {} as any;
  }
}

export default SimStatus;

